package io.gitee.macxiang.utils;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

/** 文件相关;包括生成二维码 */
public class Files extends Http {
  /** 运行系统命令 */
  public static void run(String cmd) throws Exception {
    String[] cmds = { cmd };
    Process p = Runtime.getRuntime().exec(cmds);
    InputStream stderr = p.getErrorStream();
    InputStreamReader isr = new InputStreamReader(stderr);
    BufferedReader br = new BufferedReader(isr);
    String line = null;

    StringBuffer sb = new StringBuffer();
    while ((line = br.readLine()) != null) {
      sb.append(line);
    }
  }

  /** 判断文件是否图片 */
  public static boolean isImg(File input) {
    try {
      String s = ImageIO.getImageReaders(ImageIO.createImageInputStream(input)).next().getFormatName();
      return !Datas.isEmpty(s);
    } catch (Exception e) {
    }
    return false;
  }

  /**以webp格式写图片文件
   * @param input   数据流
   * @param outPath 输出路径(可以是相对路径)
   * @param width   图片宽度,默认1024px
   * @return        成功返回输出的绝对路径,否则返回null;
   */
  public static String writeImg(InputStream input, String outPath, Integer width) {
    try {
      if (width == null || width < 10) {
        width = 1024;
      }
      final String outFormat = "webp";
      int i = outPath.lastIndexOf('.');
      outPath = formatFilename((i > 0 ? outPath.substring(0, i) : outPath) + ".webp", true);
      final File output = new File(outPath);
      BufferedImage image = ImageIO.read(input);
      if (image.getWidth() > width) {
        final int height = (int) Math.round(image.getHeight() * (width / (double) image.getWidth()));
        BufferedImage tmp = new BufferedImage(width, height, image.getType());
        // 使用 Graphics2D 绘制缩放后的图像
        Graphics2D g2d = tmp.createGraphics();
        g2d.drawImage(image, 0, 0, width, height, null);
        g2d.dispose();
        image = tmp;
      }
      // 创建 WebP 输出流
      FileOutputStream fos = new FileOutputStream(output);
      boolean result = ImageIO.write(image, outFormat, fos);
      fos.close();
      if (result) {
        return outPath;
      }
    } catch (Exception e) {
    }

    return null;
  }

  /** 生成二维码,默认大小400 */
  public static byte[] qr(String param) {
    return qr(param, 400);
  }

  /** 生成二维码 */
  public static byte[] qr(String param, int size) {
    QRCodeWriter qrCodeWriter = new QRCodeWriter();
    try {
      BitMatrix bitMatrix = qrCodeWriter.encode(param, BarcodeFormat.QR_CODE, size, size);
      // 设置背景色为透明
      MatrixToImageConfig config = new MatrixToImageConfig(0xff000000, 0x00ffffff);
      // Path b = FileSystems.getDefault().getPath("./123.png");
      ByteArrayOutputStream b = new ByteArrayOutputStream();
      MatrixToImageWriter.writeToStream(bitMatrix, "PNG", b, config);
      return b.toByteArray();
    } catch (Exception e) {
      utils.out(e);
    }
    return null;
  }

  /**文件/文件夹是否存在
   * @param str     路径
   * @param isFile  是否判断必须是文件
   * @return        结果
   */
  public static String fileExists(String str, boolean isFile) {
    File fi = new File(str);
    String ret = null;
    try {
      ret = fi.getCanonicalPath();
    } catch (Exception e) {
    }
    return fi.exists() && (isFile ? fi.isFile() : true) ? ret : null;
  }

  /**文件/文件夹是否存在
   * @param str     路径
   * @return        不区分是否文件还是文件夹的结果
   */
  public static String fileExists(String str) {
    return fileExists(str, false);
  }

  /**格式化路径为绝对路径;并确保路径目录必须存在
   * @param str 待确认的路径(可以是相对路径)
   * @return    成功:返回绝对路径,失败返回null
   */
  public static String formatFilename(String str) {
    return formatFilename(str, true);
  }

  /**格式化路径为绝对路径
   * @param str         待确认的路径(可以是相对路径)
   * @param createPath  如果路径不存在是否创建目录
   * @return            成功:返回绝对路径,失败返回null
   */
  public static String formatFilename(String str, boolean createPath) {
    File fi = new File(str);
    String ret = null;
    try {
      ret = fi.getCanonicalPath();
      if (fi.getParentFile() != null) {
        fi = fi.getParentFile();
      }
      if (createPath && !fi.exists()) {
        fi.mkdirs();
      }
    } catch (IOException e) {
      System.out.println("formatFilename出错: " + str);
    }
    return ret;
  }

  /**读取文件
   * @param fileName  文件路径
   * @return          成功返回读取内容,失败则报错;
   */
  public static byte[] readFile(String fileName) {
    File file = new File(fileName);
    InputStream fi = null;
    long fileSize = file.length();
    if (fileSize > Integer.MAX_VALUE) {
      throw new RuntimeException("文件太大");
    }

    byte[] ret = new byte[(int) fileSize];
    try {
      // 一次读一个字节
      fi = new FileInputStream(file);
      int offset = 0, numRead = 0;
      while (offset < fileSize && (numRead = fi.read(ret, offset, (int) fileSize - offset)) >= 0) {
        offset += numRead;
      }
      fi.close();
      return ret;
    } catch (Exception e) {
      throw new RuntimeException("读取文件出错:" + fileName);
    }
  }

  /**读取文件并抓化成String
   * @param fileName  文件路径
   * @return          成功返回文件内容,失败返回Null;
   */
  public static String readFileToString(String fileName) {
    String ret = null;
    try {
      ret = new String(readFile(fileName));
    } catch (Exception e) {
    }
    return ret;
  }

  /**写数据到文件
   * @param fileName  文件路径
   * @param data      欲写入的数据
   * @return          成功返回true;
   */
  public static boolean writeFile(String fileName, String data) {
    return data != null ? writeFile(fileName, data.getBytes()) : false;
  }

  /**写数据到文件
   * @param fileName  文件路径
   * @param data      欲写入的数据
   * @return          成功返回true;
   */
  public static boolean writeFile(String fileName, byte[] data) {
    try {
      if (data != null) {
        FileOutputStream fos = new FileOutputStream(formatFilename(fileName));
        fos.write(data);
        fos.close();
        return true;
      }
    } catch (Exception e) {
      System.out.println("写文件 错误");
      System.out.println(e);
    }
    return false;
  }

  /**根据路径删除指定的目录或文件，无论存在与否
   * @param sPath 要删除的目录或文件
   * @return 删除成功返回 true，否则返回 false。
   */
  public static boolean DeleteFolder(String sPath) {
    boolean flag = false;
    File file = new File(sPath);
    // 判断目录或文件是否存在
    if (!file.exists()) { // 不存在返回 false
      return flag;
    } else {
      // 判断是否为文件
      if (file.isFile()) { // 为文件时调用删除文件方法
        return deleteFile(sPath);
      } else { // 为目录时调用删除目录方法
        return deleteDirectory(sPath);
      }
    }
  }

  /**删除单个文件
   * @param sPath 被删除文件的文件名
   * @return 单个文件删除成功返回true，否则返回false
   */
  public static boolean deleteFile(String sPath) {
    boolean flag = false;
    File file = new File(sPath);
    // 路径为文件且不为空则进行删除
    if (file.isFile() && file.exists()) {
      file.delete();
      flag = true;
    }
    return flag;
  }

  /**删除目录（文件夹）以及目录下的文件
   * @param sPath 被删除目录的文件路径
   * @return 目录删除成功返回true，否则返回false
   */
  public static boolean deleteDirectory(String sPath) {
    // 如果sPath不以文件分隔符结尾，自动添加文件分隔符
    if (!sPath.endsWith(File.separator)) {
      sPath = sPath + File.separator;
    }
    File dirFile = new File(sPath);
    // 如果dir对应的文件不存在，或者不是一个目录，则退出
    if (!dirFile.exists() || !dirFile.isDirectory()) {
      return false;
    }
    boolean flag = true;
    // 删除文件夹下的所有文件(包括子目录)
    File[] files = dirFile.listFiles();
    for (int i = 0; i < files.length; i++) {
      // 删除子文件
      if (files[i].isFile()) {
        flag = deleteFile(files[i].getAbsolutePath());
        if (!flag)
          break;
      } // 删除子目录
      else {
        flag = deleteDirectory(files[i].getAbsolutePath());
        if (!flag)
          break;
      }
    }
    if (!flag)
      return false;
    // 删除当前目录
    if (dirFile.delete()) {
      return true;
    } else {
      return false;
    }
  }

}
